home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Simple Help
/
SimpleHelp.c
< prev
next >
Wrap
Text File
|
1994-09-15
|
18KB
|
741 lines
/****************************************************************************************
*
*
* SimpleHelp.c -implementation of simplehelp system
*
* ©1994, Graham Cox. All Rights Reserved.
*
* 9/2/94
* 28/6/94 First List Item now Hilited on startup of help
* Hypertext links now track mouse like buttons
* Links flash on mouse up like menus
* SetHyperlinkMatchStyle now correctly sets up data internally
* Buttons now correctly hilited at startup of help
* Now handles up and down arrow keys to select topics
* 29/6/94 Manages resource file correctly for when help is a seperate file
* 14/7/94 Reworked to compile under CodeWarrior for the PowerPC
*
****************************************************************************************/
#include "SimpleHelpTypes.h"
#include "ColourPopUp.h"
#include <Errors.h>
#define SetHiliteMode() LMSetHiliteMode(LMGetHiliteMode() &! hiliteBit)
Handle GetUniversalFunctionHandle(ProcPtr functionAddress,ProcInfoType pInfo);
ListHandle gTopicList = NIL;
void DrawHelpTopicCell(Cell tCell,short dataLen,ListHandle theList,Rect *dCell);
#define helpTopicTitle 7
OSErr BuildTopicList(SimHelpHdl sHelp,Rect *listBox)
{
/* this function builds the topic list. It first creates the list manager record and
installs the custom draw proc. It then opens the resource which defines the topic
list, and reads in each entry, storing it in the list. The sHelp record (which
must already have been created and otherwise initialised) has the list handle
stored into it. */
ListHandle theList;
topicListHdl topicList;
topicRecPtr topicEntry;
Rect dataBounds;
Point cSize;
short index;
Cell curCell;
short recSize;
OSErr btErr = memFullErr;
Handle funcLDEF;
if (sHelp != NIL)
{
topicList = (topicListHdl) GetResource(topicListResType,(*sHelp)->topicID);
btErr = ResError();
if (topicList != NIL)
{
HNoPurge((Handle) topicList);
SetRect(&dataBounds,0,0,1,(*topicList)->nTopics + 1);
SetPt(&cSize,0,0);
listBox->right -= 15; // allow for scrollbar
InsetRect(listBox,1,1); // and frame
theList = LNew(listBox,&dataBounds,cSize,0,(*sHelp)->ownerWindow,
FALSE,FALSE,FALSE,TRUE);
InsetRect(listBox,-1,-1);
listBox->right += 15;
if (theList != NIL)
{
MoveHHi((Handle) topicList);
HLock((Handle) topicList);
topicEntry = &(*topicList)->theTopic;
curCell.v = 0;
curCell.h = 0;
for(index = 0;index <= (*topicList)->nTopics;index++)
{
recSize = sizeof(topicRec) + topicEntry->topicString - 1;
LSetCell((Ptr) topicEntry,recSize,curCell,theList);
topicEntry = (topicRecPtr)((long)topicEntry + recSize);
// point at next record (variable size)
curCell.v++;
}
HUnlock((Handle) topicList);
(*sHelp)->topicList = theList; // set record field
funcLDEF = GetUniversalFunctionHandle((ProcPtr)TopicListDef,uppListDefProcInfo);
(*theList)->listDefProc = funcLDEF;
(*theList)->userHandle = (Handle) sHelp;
}
HPurge((Handle) topicList);
ReleaseResource((Handle) topicList);
}
}
return(btErr);
}
SimHelpHdl GetNewHelpDialog(short DialogID,short listItem,short textItem,short topicID)
{
/* highish level call to get the dialog and build its internal data structures. These
include the scrolling text panel and the topic list */
SimHelpHdl sHelp = NIL;
DialogPtr helpDialog;
OSErr btErr;
Rect rViewRect;
short itemType;
Handle itemHand;
GrafPtr savePort;
Cell firstCell = {0,0};
static UserItemUPP gMyUserItemUPP;
helpDialog = GetTextDialog(DialogID,textItem,defaultTextResID,FALSE);
if (helpDialog != NIL)
{
sHelp = (SimHelpHdl) NewHandleClear(sizeof(SimHelpRecord));
if (sHelp != NIL)
{
(*sHelp)->ownerWindow = helpDialog;
(*sHelp)->topicID = topicID;
(*sHelp)->indentPixels = defaultIndent;
(*sHelp)->listItemID = listItem;
(*sHelp)->textItemID = textItem;
(*sHelp)->helpResFileRef = -1;
(*sHelp)->swapResFileRef = -1;
GetPort(&savePort);
SetPort(helpDialog);
TextFont(defaultFont);
TextSize(defaultFontSize);
(*sHelp)->missingPageID = pageMissingStrID;
(*sHelp)->fontID = defaultFont;
(*sHelp)->fontSize = defaultFontSize;
(*sHelp)->hyperTextStyle = bold; // default style for hypertext
(*sHelp)->hyperTextIndex = -1; // index not initialised
GetDItem(helpDialog,listItem,&itemType,&itemHand,&rViewRect);
if ((itemType & 0x7F) == userItem)
{
btErr = BuildTopicList(sHelp,&rViewRect);
gTopicList = (*sHelp)->topicList; // annoyingly necessary...
gMyUserItemUPP = NewUserItemProc((ProcPtr) ListDrawProc);
itemHand = (Handle) gMyUserItemUPP;
SetDItem(helpDialog,listItem,itemType,itemHand,&rViewRect);
}
LDoDraw(TRUE,gTopicList);
LActivate(TRUE,gTopicList);
LSetSelect(TRUE,firstCell,(*sHelp)->topicList);
TextFont(0);
TextSize(12);
SwitchPage(sHelp,(*sHelp)->lastSelection);
itemType = helpPrevButton;
HandleHelpItems(sHelp,&itemType);
SetPort(savePort);
}
}
return(sHelp);
}
void DisposeHelpDialog(SimHelpHdl sHelp)
{
/* disposes of everything, including dialog box itself */
Handle defProc;
if (sHelp != NIL)
{
defProc = (*(*sHelp)->topicList)->listDefProc;
LDispose((*sHelp)->topicList);
gTopicList = NIL;
DisposHandle(defProc);
DisposeTDDialog((*sHelp)->ownerWindow);
DisposHandle((Handle) sHelp);
}
}
void SetHyperlinkMatchStyle(SimHelpHdl sHelp,short theStyle)
{
short hypIndex;
TEHandle teRec;
if (sHelp != NIL)
{
(*sHelp)->hyperTextStyle = theStyle;
teRec = GetTDTextHdl((*sHelp)->ownerWindow);
hypIndex = GetHypertextStyleRef(teRec,theStyle);
(*sHelp)->hyperTextIndex = hypIndex;
}
}
DialogPtr GetHelpDialogHdl(SimHelpHdl sHelp)
{
/* returns the dialog field */
if (sHelp != NIL)
return((*sHelp)->ownerWindow);
else
return(NIL);
}
void ModalHelpDialog(SimHelpHdl sHelp,short *theItem)
{
/* call replacement for ModalDialog. Handles list and text clicks, etc */
while (*theItem == 0)
{
ModalTDDialog((short*)theItem);
HandleHelpItems(sHelp,theItem);
}
}
Boolean HandleHelpItems(SimHelpHdl sHelp,short *theItem)
{
Point mClick;
GrafPtr savePort;
Cell lastOne,thisOne;
short itemType;
Handle itemHand;
Rect itemBox;
Boolean retCode = FALSE;
thisOne = lastOne = (*sHelp)->lastSelection;
if (*theItem == (*sHelp)->listItemID)
{
*theItem = 0;
/* handle list clicks */
GetPort(&savePort);
SetPort((*sHelp)->ownerWindow);
GetMouse(&mClick);
LClick(mClick,0,(*sHelp)->topicList);
SetPt(&thisOne,0,0);
if (LGetSelect(TRUE,&thisOne,(*sHelp)->topicList))
{
if (!EqualPt(thisOne,lastOne))
{
SwitchPage(sHelp,thisOne);
(*sHelp)->lastSelection = thisOne;
}
}
SetPort(savePort);
retCode = TRUE;
}
else
{
switch (*theItem)
{
case helpNextButton:
thisOne.v++;
if (thisOne.v >= (*(*sHelp)->topicList)->dataBounds.bottom)
thisOne.v = (*(*sHelp)->topicList)->dataBounds.bottom -1;
else
{
LSetSelect(FALSE,lastOne,(*sHelp)->topicList);
LSetSelect(TRUE,thisOne,(*sHelp)->topicList);
LAutoScroll((*sHelp)->topicList);
(*sHelp)->lastSelection = thisOne;
SwitchPage(sHelp,thisOne);
}
*theItem = 0;
retCode = TRUE;
break;
case helpPrevButton:
thisOne.v--;
if (thisOne.v < 0)
thisOne.v = 0;
else
{
LSetSelect(FALSE,lastOne,(*sHelp)->topicList);
LSetSelect(TRUE,thisOne,(*sHelp)->topicList);
LAutoScroll((*sHelp)->topicList);
(*sHelp)->lastSelection = thisOne;
SwitchPage(sHelp,thisOne);
}
*theItem = 0;
retCode = TRUE;
break;
}
}
if (thisOne.v == (*(*sHelp)->topicList)->dataBounds.bottom -1)
{
GetDItem((*sHelp)->ownerWindow,
helpNextButton,&itemType,&itemHand,&itemBox);
HiliteControl((ControlHandle)itemHand,255);
}
if (thisOne.v != 0)
{
GetDItem((*sHelp)->ownerWindow,
helpPrevButton,&itemType,&itemHand,&itemBox);
HiliteControl((ControlHandle) itemHand,0);
}
if (thisOne.v == 0)
{
GetDItem((*sHelp)->ownerWindow,
helpPrevButton,&itemType,&itemHand,&itemBox);
HiliteControl((ControlHandle)itemHand,255);
}
if (thisOne.v != (*(*sHelp)->topicList)->dataBounds.bottom -1)
{
GetDItem((*sHelp)->ownerWindow,
helpNextButton,&itemType,&itemHand,&itemBox);
HiliteControl((ControlHandle) itemHand,0);
}
return(retCode);
}
pascal void ListDrawProc(WindowPtr theWindow,short theItem)
{
/* user item draw proc for the list. a TAD tricky because we haven't got the list
handle- the refcon field is already in use by the scroll panel... */
short itemType;
Handle itemHand;
Rect itemBox;
RGBColor saveBack;
GetDItem(theWindow,theItem,&itemType,&itemHand,&itemBox);
if (gTopicList != NIL)
LUpdate(qd.thePort->visRgn,gTopicList);
Frame3DRect(&itemBox,FALSE); // Godrilla Mincefriend
FrameRect(&itemBox);
}
void SwitchPage(SimHelpHdl sHelp,Cell clickCell)
{
/* looks up the text page for the given cell and installs that page */
ListHandle theList;
short dLen;
bigTopicRec btRec;
if (sHelp != NIL)
{
theList = (*sHelp)->topicList;
dLen = sizeof(bigTopicRec);
LGetCell(&btRec,&dLen,clickCell,theList);
InstallPage(sHelp,btRec.pageID);
ParamText(btRec.topicString,NIL,NIL,NIL);
}
}
void InstallPage(SimHelpHdl sHelp,short pageID)
{
Handle theText;
StringHandle errStr;
TEHandle teRec;
short hypIndex;
if (sHelp != NIL)
{
if (pageID != 0)
{
SwapHelpResFile(sHelp,kSwapHelpIn);
theText = GetResource('TEXT',pageID); // preflight check
if (theText == NIL)
{
errStr = GetString((*sHelp)->missingPageID);
TDSetText((*sHelp)->ownerWindow,((Ptr)*errStr) +1,*errStr[0],NIL);
(*sHelp)->curPageResID = 0;
}
else
{
TDSetResourceText((*sHelp)->ownerWindow,pageID);
(*sHelp)->curPageResID = pageID;
}
teRec = GetTDTextHdl((*sHelp)->ownerWindow);
hypIndex = GetHypertextStyleRef(teRec,(*sHelp)->hyperTextStyle);
(*sHelp)->hyperTextIndex = hypIndex;
SwapHelpResFile(sHelp,kSwapHelpOut);
}
else
{
TDSetText((*sHelp)->ownerWindow,(Ptr)*errStr,0,NIL);
(*sHelp)->curPageResID = 0;
}
}
}
pascal Boolean ModelessHelp(SimHelpHdl sHelp,short theItem,EventRecord *theEvent)
{
/* handles a modeless help window. Call after DialogSelect if it returns TRUE. */
Boolean mtResult;
short htLink;
char keyHit;
if (sHelp != NIL)
{
mtResult = ModelessText((*sHelp)->ownerWindow,theItem,theEvent);
if (! mtResult)
{
switch(theEvent->what)
{
case mouseDown:
if (! HandleHelpItems(sHelp,&theItem))
{
htLink = ResolveHypertextLink(sHelp,theEvent->where);
if (htLink != -1)
{
/* we clicked a hypertext link. Unhilite the pick list and switch to
the page resolved */
LSetSelect(FALSE,(*sHelp)->lastSelection,(*sHelp)->topicList);
(*sHelp)->lastSelection.v = -1;
InstallPage(sHelp,htLink);
}
return(FALSE);
}
else
return(TRUE);
break;
case keyDown:
case autoKey:
keyHit = theEvent->message & charCodeMask;
switch (keyHit)
{
case 0x1F: //down arrow
theItem = helpNextButton;
HandleHelpItems(sHelp,&theItem);
break;
case 0x1E: // up arrow
theItem = helpPrevButton;
HandleHelpItems(sHelp,&theItem);
break;
}
return(TRUE);
break;
}
}
}
else
return(FALSE);
}
/*--------------------------------------------------------------------------------------*/
pascal void TopicListDef(short lMsg,Boolean lSelect,Rect *lRect,Cell lCell,
short lDataOffset,short lDataLength,ListHandle theList)
{
/* this is the list definition function for th topic list. It is embedded in this code
and will be called by the list manager using a 'sneaky jump'. */
switch (lMsg)
{
case lInitMsg:
break;
case lDrawMsg:
DrawHelpTopicCell(lCell,lDataLength,theList,lRect);
if (!lSelect)
break;
case lHiliteMsg:
SetHiliteMode();
InvertRect(lRect);
break;
case lCloseMsg:
break;
}
}
void DrawHelpTopicCell(Cell tCell,short dataLen,ListHandle theList,Rect *dCell)
{
short tLen;
bigTopicRecPtr tData;
bigTopicRec tRec;
FontInfo fInfo;
SimHelpHdl sHelp;
short indentPix,saveFont,saveSize;
sHelp = (SimHelpHdl)(*theList)->userHandle; // might be handy...
tLen = dataLen;
tData = &tRec;
LGetCell((Ptr) tData,&tLen,tCell,theList);
saveFont = qd.thePort->txFont;
saveSize = qd.thePort->txSize;
TextFont(geneva);
TextSize(9);
GetFontInfo(&fInfo);
indentPix = tRec.indent * (*sHelp)->indentPixels;
MoveTo(dCell->left + indentPix + 1,dCell->top + fInfo.ascent - 1);
TextFace(tRec.tStyle);
DrawText(&tData->topicString,1,tData->topicString[0]);
TextFace(0);
TextFont(saveFont);
TextSize(saveSize);
}
short GetHypertextStyleRef(TEHandle teRec,short theStyle)
{
/* returns an index value which represents the index in the StyleRun table of the
entry which matches the given style. You need this to look up a hypertext link.
Call after installing a new page. */
TEStyleHandle teStyles;
short i;
STHandle styleTab;
STElement styleEntry;
if (teRec != NIL)
{
teStyles = GetStylHandle(teRec);
if (teStyles != NIL)
{
styleTab = (*teStyles)->styleTab;
for (i = 0; i < (*teStyles)->nStyles; i++)
{
styleEntry = (*styleTab)[i];
if (styleEntry.stFace == theStyle)
return(i);
}
}
}
return(-1);
}
short ResolveHypertextLink(SimHelpHdl sHelp,Point clickPt)
{
/* call after clicking in the text pane. This function resolves whether a hypertext
link was clicked, handling all of the necessary feedback, such as highlighting,
etc. It returns the page ID of the link, or -1 if not a link or page missing */
TEHandle teRec;
short charOffset,index,hypTextLen,pageRef = -1,i;
GrafPtr savePort;
TEStyleHandle teStyles;
StyleRun stRun,nextRun;
long ignored;
Ptr textStart;
Str255 hyperString;
htIndexHdl hypIndex;
Rect wordRect;
Boolean trackPointIn,wasIn;
if (sHelp != NIL && (*sHelp)->hyperTextIndex != -1)
{
teRec = GetTDTextHdl((*sHelp)->ownerWindow);
if (teRec != NIL)
{
teStyles = GetStylHandle(teRec);
if (teStyles != NIL)
{
GetPort(&savePort);
SetPort((*sHelp)->ownerWindow);
GlobalToLocal(&clickPt);
charOffset = TEGetOffset(clickPt,teRec);
if (charOffset > 0 && charOffset < (*teRec)->teLength)
{
// scan the runs array for the style of the clicked char
for (index = 0;index < (*teStyles)->nRuns;index++)
{
stRun = (*teStyles)->runs[index];
nextRun = (*teStyles)->runs[index +1];
if (charOffset >= stRun.startChar && charOffset < nextRun.startChar)
{
/* we found the char. Does the hypertext index match? */
if (stRun.styleIndex == (*sHelp)->hyperTextIndex)
{
/* yes- fetch the string and hilite it */
TESetSelect(stRun.startChar,nextRun.startChar,teRec);
TEActivate(teRec);
wordRect = (*teRec)->selRect;
/* track the rect as long as the mouse is down */
trackPointIn = wasIn = TRUE;
while (WaitMouseUp())
{
GetMouse(&clickPt);
trackPointIn = PtInRect(clickPt,&wordRect);
if (trackPointIn &! wasIn)
TEActivate(teRec);
else
if (wasIn &! trackPointIn)
TEDeactivate(teRec);
wasIn = trackPointIn;
}
if (wasIn)
{
for(i = 0;i < 3;i++)
{
TEDeactivate(teRec);
Delay(5,&ignored);
TEActivate(teRec);
Delay(5,&ignored);
}
TEDeactivate(teRec);
hypTextLen = nextRun.startChar - stRun.startChar;
textStart = (Ptr)(*(*teRec)->hText + stRun.startChar);
BlockMove(textStart,&hyperString[1],hypTextLen);
hyperString[0] = hypTextLen;
/* now search the hypertext index for this page to match
this string */
hypIndex = (htIndexHdl) GetResource(hyperTextIndexType,
(*sHelp)->curPageResID);
if (hypIndex != NIL)
{
pageRef = MatchHyperLink(&hyperString,hypIndex);
HPurge((Handle) hypIndex);
ReleaseResource((Handle) hypIndex);
}
}
break; // jump out of the loop
}
}
}
}
SetPort(savePort);
}
}
}
return(pageRef);
}
short MatchHyperLink(Str255 *testStr,htIndexHdl pageIndex)
{
/* searches the page index record for a match with the test string. When found, the
corresponding page ID is returned, otherwise, -1 is returned */
Ptr recStart;
long recSize;
short recCount,index;
hypEntry curEntry;
if (pageIndex != NIL)
{
HNoPurge((Handle) pageIndex);
recCount = (*pageIndex)->nEntries;
MoveHHi((Handle) pageIndex);
HLock((Handle) pageIndex);
recStart = (Ptr) &(*pageIndex)->hEntries[0];
for (index = 0;index < recCount;index++)
{
recSize = sizeof(hypEntry);
BlockMove(recStart,&curEntry,recSize);
if (EqualString(*testStr,curEntry.htMatchString,FALSE,FALSE))
{
HUnlock((Handle) pageIndex);
return(curEntry.pageID);
}
recSize = curEntry.htMatchString[0] + sizeof(short) + 1;
recStart += recSize;
}
HUnlock((Handle) pageIndex);
}
return(-1);
}
void SetHelpResFile(SimHelpHdl sHelp,short refNum)
{
/* sets the help file resource refnum */
(*sHelp)->helpResFileRef = refNum;
}
void SwapHelpResFile(SimHelpHdl sHelp,Boolean hSwapContext)
{
/* switches the resource files according to the flag */
if (hSwapContext == kSwapHelpIn)
{
(*sHelp)->swapResFileRef = CurResFile();
if ((*sHelp)->helpResFileRef != -1)
UseResFile((*sHelp)->helpResFileRef);
}
else
{
if ((*sHelp)->swapResFileRef != -1)
UseResFile((*sHelp)->swapResFileRef);
}
}